package com.heima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.mapper.AdSensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.wemedia.WemediaFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @作者 itcast
 * @创建日期 2021/4/17 9:43
 **/
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {
    @Value("${file.oss.web-site}")
    String webSite;

    @Autowired
    WemediaFeign wemediaFeign;


    @Override
    public void autoScanByMediaNewsId(Integer wmNewsId) {
        // 1. 检查id是否正确
        if(wmNewsId==null){
            log.error("自动审核失败, 文章id==> {}",wmNewsId);
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"自媒体文章ID不存在");
        }
        // 2. 根据文章ID查询自媒体文章信息
        WmNews wmNews = wemediaFeign.findById(wmNewsId);
        if(wmNews == null){
            log.error("自动审核失败, 待审核的文章为空==> {}",wmNewsId);
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"待审核的文章为空");
        }
        // 3. 检查文章状态是否为4  并且发布时间是否小于等于当前时间
        long now = System.currentTimeMillis();
        if (wmNews.getStatus().equals(WmNews.Status.ADMIN_SUCCESS.getCode())&&wmNews.getPublishTime().getTime()<=now) {
            // 3.1 true:   调用发布文章方法  return;
            publishArticle(wmNews);
            return;
        }
        // 4. 检查文章状态是否为8  并且发布时间是否小于等于当前时间
        if (wmNews.getStatus().equals(WmNews.Status.SUCCESS.getCode())&&wmNews.getPublishTime().getTime()<=now) {
            // 4.1 true:   调用发布文章方法  return;
            publishArticle(wmNews);
            return;
        }
        // 5. 检查文章状态是否为1  待审核状态 需要审核
        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            // 抽取文章内容中的 所有文本 (string)  和  所有图片(List<url>)  Map  key: content   images
            Map<String,Object> contentAndImages = handleTextAndImages(wmNews);
            //基于敏感词 使用DFA算法进行敏感词审核 不通过: 文章状态改为2
            boolean sensitiveScan = handleSensitive((String)contentAndImages.get("content"),wmNews);
            if(!sensitiveScan) return;
            //基于阿里云内容安全   进行文本内容审核  不通过: 文章状态改为2  不确定: 文章状态改为3
            boolean textScan = handleTextScan((String)contentAndImages.get("content"),wmNews);
            if(!textScan) return;
            //基于阿里云内容安全   进行图片内容审核  不通过: 文章状态改为2  不确定: 文章状态改为3
            boolean imageScan = handleImageScan((List<String>)contentAndImages.get("images"),wmNews);
            if(!imageScan) return;
            // 审核通过 判断文章发布时间是否大于当前时间  文章状态改为8
            if(wmNews.getPublishTime().after(new Date())){
                updateWmNews(wmNews,(short)8,"审核通过");
                return;
            }
            // 审核通过 发布文章方法
            publishArticle(wmNews);
        }
    }

    @Autowired
    GreenImageScan greenImageScan;
    /**
     * 阿里云图片审核
     * @param images 待审核图片
     * @param wmNews 文章
     * @return
     */
    private boolean handleImageScan(List<String> images, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String)map.get("suggestion");
            if("block".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)2,"图片中有违规信息");
            }else if("review".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)3,"图片中有不确定的信息，转为人工审核");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云图片审核失败 文章id==>{}    原因==>{}",wmNews.getId(),e);
            flag = false;
            updateWmNews(wmNews,(short)3,"阿里云图片审核失败，转为待人工审核");
        }
        return flag;
    }

    @Autowired
    GreenTextScan greenTextScan;

    /**
     * 阿里云 文本审核
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleTextScan(String content, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String)map.get("suggestion");
            if("block".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)2,"文本内容中有违规信息");
            }else if("review".equals(suggestion)){
                flag = false;
                updateWmNews(wmNews,(short)3,"文本内容中有不确定的信息，转为人工审核");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云文本失败 文章id==>{}    原因==>{}",wmNews.getId(),e);
            flag = false;
            updateWmNews(wmNews,(short)3,"阿里云文本审核失败，转为待人工审核");
        }
        return flag;
    }

    @Autowired
    AdSensitiveMapper adSensitiveMapper;
    /**
     * 敏感词审核
     * @param content 待审核内容
     * @param wmNews  文章信息
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        boolean flag = true;
        // 1. 查询数据库中敏感词列表
        // TODO  待优化  每次查询数据库获取敏感词 性能低  可以通过redis做缓存来优化
        List<String> allSensitive = adSensitiveMapper.findAllSensitive();
        // 2. 使用DFA工具类 生成 DFA数据模型
        SensitiveWordUtil.initMap(allSensitive);
        // 3. 使用DFA工具类 检查是否包含敏感词 {"小狗":1,"法论功":2}
        Map<String, Integer> sensitiveResult = SensitiveWordUtil.matchWords(content);
        if(sensitiveResult!=null && !sensitiveResult.isEmpty()){
            // 4. 如果包含敏感词  则修改wmNews状态为2
            flag = false;
            updateWmNews(wmNews,(short)2,"文章文本内容包含敏感词 : "+sensitiveResult);
        }
        return flag;
    }

    /**
     * 修改自媒体文章信息
     * @param wmNews
     * @param status
     * @param reason
     */
    private void updateWmNews(WmNews wmNews, Short status, String reason) {
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
        if(responseResult.getCode() != 0){
            log.error("修改自媒体文章状态失败 id: {}",wmNews.getId());
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,responseResult.getErrorMessage());
        }
    }

    /**
     * 抽取文章中所有 文本内容和 图片内容
     * @param wmNews
     * @return
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        // 文章内容的json字符串转为 List<Map> 集合
        String contentJson = wmNews.getContent();
        List<Map> contentMap = JSON.parseArray(contentJson, Map.class);
        if(contentMap == null || contentJson.isEmpty()){
            log.error("自动审核失败, 待审核的文章内容为空==> {}",wmNews.getId());
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"自动审核失败, 待审核的文章内容为空");
        }
        // 1. 抽取文本  (文章标题   文章内容)  [{type:text,value:内容}     ]   冥想中_hmtt_美丽的姑娘        中美  敏感
        String content = contentMap.stream()
                .filter(map -> map.get("type").equals("text"))// 过滤数据
                .map(map -> (String) map.get("value"))
                .collect(Collectors.joining("_hmtt_"));
        content = wmNews.getTitle() + "_hmtt_"+content;
        // 2. 抽取内容图片
        List<String> imageList = contentMap.stream()
                .filter(map -> map.get("type").equals("image"))// 过滤数据
                .map(map -> (String) map.get("value"))
                .distinct()
                .collect(Collectors.toList());
        // 3. 解析封面图片   images =   url1.jpg,url2.jpg
        String images = wmNews.getImages();
        if (StringUtils.isNotBlank(images)) {
            Arrays.stream(images.split(","))
                    .map(url->webSite+url)
                    .distinct()
                    .forEach(imageList::add);
        }
        // 4. 封装返回结果
        Map<String, Object> resultMap = new HashMap();
        resultMap.put("content",content);  // 文本内容
        resultMap.put("images",imageList);  // 图片内容
        return resultMap;
    }
    /**
     * 发布文章
     * @param wmNews
     */
    private void publishArticle(WmNews wmNews) {
        //远程调用article微服务保存相关文章信息
        ResponseResult responseResult = saveArticle(wmNews);
        if(responseResult.getCode()!=0){
            log.error("远程调用文章微服务 保存文章方法出错==>{}",responseResult.getErrorMessage());
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,responseResult.getErrorMessage());
        }
        // 保存文章的方法，一定要返回articleId 把id设置到wm_news
        wmNews.setArticleId((Long)responseResult.getData());
        updateWmNews(wmNews,(short)9,"审核通过");
        // ==============新加代码==============================================
        Map map = new HashMap();
        map.put("articleId",wmNews.getArticleId());
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSON.toJSONString(map));
        // ==============新加代码==============================================
    }
    @Autowired
    KafkaTemplate kafkaTemplate;

    @Autowired
    AdChannelMapper adChannelMapper;
    @Autowired
    ArticleFeign articleFeign;

    /**
     * 远程调用article服务 保存文章(3张表)
     * @param wmNews
     * @return
     */
    private ResponseResult saveArticle(WmNews wmNews) {
        // 封装调用的dto参数
        ArticleDto articleDto = new ArticleDto();
        // 拷贝wmnews相关属性到articleDto中
        BeanUtils.copyProperties(wmNews,articleDto);
        // 文章有可能已经发布过，但下架后还可以对文章进行修改，修改后重新审核 那么 对应article中 该文章是存在的
        articleDto.setId(wmNews.getArticleId());//wmNews==>article     下架 ==> 1 ==> 审核
        articleDto.setLayout(wmNews.getType());// 0 无图 1 单图  2 3 多图
        articleDto.setFlag((byte)0);
        // 补全频道信息
        Integer channelId = wmNews.getChannelId();
        AdChannel channel = adChannelMapper.selectById(channelId);
        if(channel!=null){
            // 设置频道名称
            articleDto.setChannelName(channel.getName());
        }
        // ap_author 表 ==> article服务   ap_user wm_user ap_author (name)
        WmUser wmUser = wemediaFeign.findWmUserById(wmNews.getUserId());
        if(wmUser == null){
            log.error("远程调用文章微服务 该文章关联的自媒体用户不存在  文章ID={}  自媒体用户ID={}",wmNews.getId(),wmNews.getUserId());
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"该文章关联的自媒体用户不存在");
        }
        articleDto.setAuthorName(wmUser.getName());
        // 设置文章内容
//        articleDto.setContent(wmNews.getContent());
        // 保存文章方法  返回文章的id
        return articleFeign.saveArticle(articleDto);
    }
}
